import * as turf from '@turf/turf'
import { Feature, Polygon, Position } from 'geojson'
import Konva from 'konva'
import { KonvaEventObject } from 'konva/lib/Node'
import { useEffect, useRef, useState } from 'react'
import { Layer, Rect, Shape, Stage } from 'react-konva'

interface IRectPoint {
  x1: number
  y1: number
  x2: number
  y2: number
}

interface IShapePoint {
  type: 'MultiPolygon' | 'Polygon'
  coordinates: Position[][][] | Position[][]
}

type FeaturePolygon = Feature<
  Polygon,
  {
    [name: string]: any
  }
>

// 测试数据
const initShapePoint: IShapePoint[] = [
  {
    coordinates: [
      [
        [
          [628, 139],
          [1883, 139],
          [1883, 963],
          [628, 963],
          [628, 139]
        ],
        [
          [771, 240],
          [771, 810],
          [1696, 810],
          [1696, 240],
          [771, 240]
        ]
      ]
    ],
    type: 'MultiPolygon'
  },
  {
    coordinates: [
      [
        [30, 25],
        [410, 25],
        [410, 431],
        [30, 431],
        [30, 25]
      ]
    ],
    type: 'Polygon'
  }
]

export default function View() {
  const stageRef = useRef<Konva.Stage>()
  const rectRef = useRef<Konva.Rect>()
  const selecting = useRef(false)

  const [rectPoint, setRectPoint] = useState<IRectPoint>({ x1: 0, y1: 0, x2: 0, y2: 0 })
  const [shapePoint, setShapePoint] = useState<IShapePoint[]>(initShapePoint)

  // 测试数据
  useEffect(() => {
    console.log('🚀 ~ useEffect ~ shapePoint:', shapePoint)
  }, [shapePoint])

  // 鼠标左键点击画布
  const selectionRectMouseDown = (e: KonvaEventObject<MouseEvent>) => {
    e.evt.preventDefault()

    const { x, y } = stageRef.current.getRelativePointerPosition()
    setRectPoint({ x1: x, y1: y, x2: x, y2: y })

    rectRef.current.show()
    rectRef.current.width(0)
    rectRef.current.height(0)
    selecting.current = true
  }

  // 鼠标在画布移动
  const selectionRectMouseMove = (e: KonvaEventObject<MouseEvent>) => {
    if (!selecting.current) {
      return
    }
    e.evt.preventDefault()

    const { x, y } = stageRef.current.getRelativePointerPosition()
    setRectPoint({ ...rectPoint, x1: x, y1: y })

    rectRef.current.setAttrs({
      x: Math.min(rectPoint.x1, rectPoint.x2),
      y: Math.min(rectPoint.y1, rectPoint.y2),
      width: Math.abs(rectPoint.x2 - rectPoint.x1),
      height: Math.abs(rectPoint.y2 - rectPoint.y1)
    })
  }

  // 鼠标在画布放开左键
  const selectionRectMouseUp = (e: KonvaEventObject<MouseEvent>) => {
    selecting.current = false
    if (!rectRef.current.visible()) {
      return
    }
    e.evt.preventDefault()

    // 清理无效的矩形
    const { x, y, width, height } = rectRef.current.attrs
    if (Math.min(width, height) === 0) {
      if (e.evt.shiftKey) {
        return
      } else {
        setShapePoint([])
        return
      }
    }

    // rect坐标 转成 shape坐标
    const newShapePoint: IShapePoint = {
      type: 'Polygon',
      coordinates: [
        [
          [x, y],
          [x + width, y],
          [x + width, y + height],
          [x, y + height],
          [x, y]
        ]
      ]
    }

    if (e.evt.shiftKey && shapePoint.length !== 0) {
      handleShiftKey(newShapePoint)
    } else if (e.evt.altKey) {
      handleAltKey(newShapePoint)
    } else {
      setShapePoint([newShapePoint])
    }

    // 重置rect
    rectRef.current.hide()
    setRectPoint({ x1: 0, y1: 0, x2: 0, y2: 0 })
  }

  const handleShiftKey = (newShapePoint: IShapePoint) => {
    // turf库用来计算多边形点的数组
    const turfPolyPoints = []
    const point = shapePoint.concat([newShapePoint])
    point.forEach(ele => {
      turfPolyPoints.push(getPolygonInstance(ele))
    })

    // 并集
    const union = turf.union(turf.featureCollection(turfPolyPoints))
    const coordinates = rebuildShapePoint(union)

    setShapePoint([coordinates])
  }

  const handleAltKey = (newShapePoint: IShapePoint) => {
    const newTurfPolyPoints = turf.polygon(newShapePoint.coordinates as Position[][])
    // 交集的坐标
    const intersectCoordinates = []
    shapePoint.forEach(ele => {
      const orignTurfPolyPoints = getPolygonInstance(ele) as FeaturePolygon
      const intersect = turf.intersect(turf.featureCollection([orignTurfPolyPoints, newTurfPolyPoints]))
      if (intersect !== null) {
        const coordinates = rebuildShapePoint(intersect)
        intersectCoordinates.push(coordinates)
      }
    })

    // 原图形 去除 并集部分
    const differenceCoordinates = []
    shapePoint.forEach(orign => {
      const orignTurfPolyPoints = getPolygonInstance(orign) as FeaturePolygon
      const intersectPolyGrpup = intersectCoordinates.map(ele => {
        return getPolygonInstance(ele)
      }) as FeaturePolygon[]

      if (isIntersects(orignTurfPolyPoints, intersectCoordinates)) {
        const difference = turf.difference(turf.featureCollection([orignTurfPolyPoints, ...intersectPolyGrpup]))
        if (difference !== null) {
          const coordinates = rebuildShapePoint(difference)
          differenceCoordinates.push(coordinates)
        }
      } else {
        differenceCoordinates.push(orign)
      }
    })

    setShapePoint(differenceCoordinates as IShapePoint[])
  }

  // 把turl返回值,重组自己的形状坐标
  const rebuildShapePoint = (turfObj: any): IShapePoint => {
    return {
      type: turfObj.geometry.type,
      coordinates: turfObj.geometry.coordinates
    }
  }

  // 判断"原多边形"是否和"新的多边形组"相交
  const isIntersects = (orignPoly, polyCoordinates: any[]) => {
    let isIntersect = false
    for (let index = 0; index < polyCoordinates.length; index++) {
      const element = polyCoordinates[index]
      const intersectPoly = getPolygonInstance(element)
      if (turf.booleanIntersects(orignPoly, intersectPoly)) {
        isIntersect = true
        break
      }
    }
    return isIntersect
  }

  // 返回multiPolygon\polygon的实例
  const getPolygonInstance = (element: IShapePoint) => {
    if (element.type === 'Polygon') {
      return turf.polygon(element.coordinates as Position[][])
    } else if (element.type === 'MultiPolygon') {
      return turf.multiPolygon(element.coordinates as Position[][][])
    }
  }

  // 渲染多边形
  const renderShape = () => {
    if (shapePoint === undefined) {
      return
    }

    return shapePoint.map((ele, index) => {
      return (
        <Shape
          fill='#00D2FF'
          key={index}
          stroke='black'
          strokeWidth={2}
          dash={[5, 5]}
          sceneFunc={(context, shape) => {
            context.beginPath()

            if (ele.type === 'Polygon') {
              ele.coordinates.forEach(arr => {
                arr.forEach((item, itemIndex) => {
                  const [x, y] = item
                  if (itemIndex === 0) {
                    context.moveTo(x, y)
                  } else {
                    context.lineTo(x, y)
                  }
                })
              })
            } else if (ele.type === 'MultiPolygon') {
              ele.coordinates.forEach(multiPolygonItem => {
                multiPolygonItem.forEach(arr => {
                  arr.forEach((item, itemIndex) => {
                    const [x, y] = item
                    if (itemIndex === 0) {
                      context.moveTo(x, y)
                    } else {
                      context.lineTo(x, y)
                    }
                  })
                })
              })
            }

            context.closePath()
            context.clip()
            context.fillStrokeShape(shape)
          }}
        />
      )
    })
  }

  return (
    <>
      <h1>方形选区:</h1>
      <p>类似photoshop中的矩形选框,按住shift可以增加选区;alt减少选区</p>
      <Stage
        ref={stageRef}
        width={window.innerWidth}
        height={window.innerHeight}
        onMouseDown={selectionRectMouseDown}
        onMouseMove={selectionRectMouseMove}
        onMouseUp={selectionRectMouseUp}
      >
        <Layer>
          {renderShape()}
          <Rect ref={rectRef} stroke='black' strokeWidth={2} dash={[5, 5]} listening={false} />
        </Layer>
      </Stage>
    </>
  )
}
